using System;
using Unity.Entities;
using UnityEngine;

namespace Gj
{
    public interface Aim
    {
        float GetAimRotateSpeed();
        Entity GetAimEntity();
        Entity GetEntity();
        Vector3 GetPosition();
        Quaternion GetRotation();
        Vector3 GetAimPosition();
        Quaternion GetAimRotation();
        void SetAimPosition(Vector3 position);
        void SetAimRotation(Quaternion rotation);
    }

    public class AimPart : BasePart
    {
        private Action _failNotice;
        private Vector3 _position = Vector3.zero;

        private Aim _self;

        private Action _successNotice;
        private UnityObject _target;
        private bool autoLook;

        private AimStatus status = AimStatus.Follow;
        private AimType type = AimType.None;

        public void Init(Aim target)
        {
            _self = target;
            var entity = _self.GetAimEntity();
            ObjectService.EntityManager.AddComponentData(entity, new LookStatusData { status = LookStatus.None });
        }

        private void LookTarget(UnityObject target)
        {
            if (target == null) return;
            _target = target;

            var entity = _self.GetAimEntity();
            ObjectService.EntityManager.AddComponentData(entity, new LookStatusData { status = LookStatus.Target });
            ObjectService.EntityManager.AddComponentData(entity,
                new LookData { target = target.GetEntity(), speed = _self.GetAimRotateSpeed() });
        }

        private void LookPosition(Vector3 position)
        {
            _position = position;
            var entity = _self.GetAimEntity();
            ObjectService.EntityManager.AddComponentData(entity, new LookStatusData { status = LookStatus.Position });
            ObjectService.EntityManager.AddComponentData(entity,
                new LookData { position = position, speed = _self.GetAimRotateSpeed() });
        }

        private void LookZero()
        {
            var entity = _self.GetAimEntity();
            ObjectService.EntityManager.AddComponentData(entity, new LookStatusData { status = LookStatus.Sync });
            ObjectService.EntityManager.AddComponentData(entity,
                new LookData { target = _self.GetEntity(), speed = _self.GetAimRotateSpeed() });
        }

        private void LookRotate()
        {
            var entity = _self.GetAimEntity();
            ObjectService.EntityManager.AddComponentData(entity, new LookStatusData { status = LookStatus.Rotate });
            ObjectService.EntityManager.AddComponentData(entity,
                new LookData { direction = Vector3.right, speed = _self.GetAimRotateSpeed() / 2 });
        }

        private void Stop()
        {
            _target = null;
            var entity = _self.GetAimEntity();
            ObjectService.EntityManager.AddComponentData(entity, new LookStatusData { status = LookStatus.None });
        }

        public bool IsAiming()
        {
            return status != AimStatus.Wait && status != AimStatus.Follow && status != AimStatus.Rotate;
        }

        public bool IsFollow()
        {
            return status == AimStatus.Follow;
        }

        public bool IsEnd()
        {
            return status == AimStatus.End;
        }

        public bool IsPosition()
        {
            return type == AimType.Position;
        }

        public void OnUpdate()
        {
            switch (status)
            {
                case AimStatus.Start:
                    if (type == AimType.Target)
                        if (_target == null || !_target.IsExist())
                        {
                            Stop();
                            if (_failNotice != null) _failNotice();
                            status = AimStatus.Wait;
                            return;
                        }

                    if (CheckAim())
                    {
                        if (_successNotice != null) _successNotice();
                        status = AimStatus.Ing;
                    }

                    break;
                case AimStatus.Ing:
                    break;
                case AimStatus.End:
                    if (CheckZero())
                    {
                        Stop();
                        status = AimStatus.Follow;
                    }

                    break;
                case AimStatus.Follow:
                    break;
                case AimStatus.Wait:
                    break;
                case AimStatus.Rotate:
                    break;
            }
        }

        public void Target(UnityObject target, Action success, Action fail)
        {
            _successNotice = success;
            _failNotice = fail;
            Target(target);
        }

        public void Target(UnityObject target)
        {
            LookTarget(target);
            status = AimStatus.Start;
            type = AimType.Target;
        }

        public void Position(Vector3 position, Action success, Action fail)
        {
            _successNotice = success;
            _failNotice = fail;
            Position(position);
        }

        public void Position(Vector3 position)
        {
            LookPosition(position);
            status = AimStatus.Start;
            type = AimType.Position;
        }

        public void Rotate()
        {
            LookRotate();
            status = AimStatus.Rotate;
        }

        public void Recovery()
        {
            if (type == AimType.Position)
            {
                if (status == AimStatus.End || status == AimStatus.Follow) return;
                LookZero();
                status = AimStatus.End;
            }
            else
            {
                if (status != AimStatus.Wait) return;
                LookZero();
                status = AimStatus.End;
            }
        }

        public void Follow()
        {
            status = AimStatus.Follow;
        }

        public void Clear()
        {
            switch (status)
            {
                case AimStatus.Start:
                    switch (type)
                    {
                        case AimType.Position:
                            _self.SetAimRotation(Quaternion.LookRotation(_position - _self.GetAimPosition()));
                            break;
                        case AimType.Target:
                            _self.SetAimRotation(
                                Quaternion.LookRotation(_target.GetPosition() - _self.GetAimPosition()));
                            break;
                    }

                    break;
            }

            Stop();
            status = AimStatus.Wait;
        }

        private bool CheckAim()
        {
            switch (type)
            {
                case AimType.Position:
                    return _self.GetAimRotation() == Quaternion.LookRotation(_position - _self.GetAimPosition());
                case AimType.Target:
                    return _self.GetAimRotation() ==
                           Quaternion.LookRotation(_target.GetPosition() - _self.GetAimPosition());
            }

            return false;
        }

        private bool CheckZero()
        {
            return _self.GetAimRotation() == _self.GetRotation();
        }

        private enum AimStatus
        {
            Start,
            Ing,
            End,
            Wait,
            Rotate,
            Follow
        }

        private enum AimType
        {
            None,
            Target,
            Position
        }
    }
}